#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "MSTK.h"


#ifdef __cplusplus
extern "C" {
#endif

  /* 
     wrapper functions for user

  */

int MSTK_Mesh_Partition(Mesh_ptr mesh, Mesh_ptr *submeshes, int num, int ring, int with_attr) {
  int i, j, natt;
  char attr_name[256];
  MAttrib_ptr attrib;

  MESH_Partition(mesh, submeshes, num);
  for(i = 0; i < num; i++) {
    MESH_BuildPBoundary(mesh,submeshes[i]);
    MESH_AddGhost(mesh,submeshes[i],i,ring);
  }

  if(!with_attr) 
    return 1;
  natt = MESH_Num_Attribs(mesh);
  for(j = 0; j < natt; j++) {
    attrib = MESH_Attrib(mesh,j);
    MAttrib_Get_Name(attrib,attr_name);
    for(i = 0; i < num; i++)
      MESH_CopyAttr(mesh,submeshes[i],attr_name);
  }
  printf("global mesh has been partitioned into %d parts\n",num);
  return 1;
}

int MSTK_Mesh_Concatenate(Mesh_ptr mesh, Mesh_ptr *submeshes, int num, int with_attr) {
  int j, natt;
  char attr_name[256];
  MAttrib_ptr attrib;

  MESH_Concatenate(mesh, submeshes, num);

  if(!with_attr) 
    return 1;
  natt = MESH_Num_Attribs(mesh);
  for(j = 0; j < natt; j++) {
    attrib = MESH_Attrib(mesh,j);
    MAttrib_Get_Name(attrib,attr_name);
    MESH_CollectAttr(mesh,submeshes,num,attr_name);
  }
  return 1;
}

int MSTK_SendMesh(Mesh_ptr mesh, int rank, int with_attr, MPI_Comm comm) {
    int i, natt;
    char attr_name[256];
    MAttrib_ptr attrib;

    MESH_SendMesh(mesh,rank,comm);
    if(!with_attr) 
      return 1;
    natt = MESH_Num_Attribs(mesh);
    for(i = 0; i < natt; i++) {
      attrib = MESH_Attrib(mesh,i);
      MAttrib_Get_Name(attrib,attr_name);
      MESH_SendAttr(mesh,attr_name,rank,comm);
    }
    return 1;
  }

int MSTK_RecvMesh(Mesh_ptr mesh, int dim, int send_rank, int rank, int with_attr, MPI_Comm comm) {
    int i, natt;
    char attr_name[256];
    MAttrib_ptr attrib;

    MESH_RecvMesh(mesh,dim,send_rank,rank,comm);
    if(!with_attr)
      return 1;
    natt = MESH_Num_Attribs(mesh);
    for(i = 0; i < natt; i++) {
      attrib = MESH_Attrib(mesh,i);
      MAttrib_Get_Name(attrib,attr_name);
      MESH_RecvAttr(mesh,attr_name,send_rank,rank,comm);
    }
    return 1;
  }
    
int MSTK_Mesh_Scatter(Mesh_ptr *recv_mesh, const char* global_mesh_name, int dim, int ring, int with_attr, int rank, int num, MPI_Comm comm) {
  int i, ok;
  if(rank == 0) {
    Mesh_ptr mesh = MESH_New(UNKNOWN_REP);
    ok = MESH_InitFromFile(mesh,global_mesh_name);
    if (!ok) {
      fprintf(stderr,"Cannot open input file %s\n\n\n",global_mesh_name);
      exit(-1);
    }
    fprintf(stdout,"mstk mesh %s read in successfully\n",global_mesh_name);
    
    /* Partition the mesh*/
    Mesh_ptr *submeshes = (Mesh_ptr *)MSTK_malloc((num)*sizeof(Mesh_ptr));
    MSTK_Mesh_Partition(mesh, submeshes, num, ring, with_attr);

    *recv_mesh = submeshes[0];
    for(i = 1; i < num; i++) {
      MSTK_SendMesh(submeshes[i],i,with_attr,comm);
    }
  }
  if( rank > 0) {
    MSTK_RecvMesh(*recv_mesh,dim,0,rank,with_attr,comm);
  }
  return 1;
}


int MSTK_UpdateAttr(Mesh_ptr mesh, int rank, int num,  MPI_Comm comm) {  
  int i, natt;
  char attr_name[256];
  MAttrib_ptr attrib;
  MESH_UpdateGlobalInfo(mesh, rank, num,  MPI_COMM_WORLD);
  natt = MESH_Num_Attribs(mesh);
  for(i = 0; i < natt; i++) {
    attrib = MESH_Attrib(mesh,i);
    MAttrib_Get_Name(attrib,attr_name);
    MPI_Barrier(comm);
    MESH_UpdateAttr(mesh,attr_name,rank,num,comm);
  }
  return 1;
}


#ifdef __cplusplus
}
#endif

